package com.app.service.impl;

import com.app.dao.ShareDao;
import com.app.pojo.*;
import com.app.service.IFileService;
import com.app.utils.FileLogUtils;
import com.app.utils.FileUtils;
import com.app.utils.Judges;
import com.bby.Settings;
import com.sun.org.apache.regexp.internal.RE;
import org.aspectj.apache.bcel.generic.TABLESWITCH;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Part;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.regex.Pattern;

/**
 * 需要先在功能执行前检查输入
 */
@Service
public class DiskService implements IFileService {
    ShareDao shareDao;

    public DiskService(ShareDao shareDao) {
        this.shareDao = shareDao;
    }

    /**
     * 
     * @param userId 操作者id
     * @param path 操作目标地址
     * @return: 列表
     * @author: 本小蛋
     * @time: 2021/8/30 16:13
     */
    public FileListResult getFileList(String userId, MyPath path){
        //先判断请求文件目录是否符合要求
        if (!path.isAvailable())
            return new FileListResult(Status.INPUT_NOT_REGULATED);
        System.out.println(userId+"获取列表："+path);
        String filePath=path.sourcePath;
        if (path.diskId==null)//访问自己的
            filePath=userId+path.detailPath;
        else if (!FileUtils.isSearchedOpen(userId)) return new FileListResult(Status.SHARE_SEARCH_NOT_OPEN);
        //通过验证
        File file=Paths.get(Settings.rootsDir,filePath).toFile();//path中有id
        if (!file.exists()) return new FileListResult(Status.FILE_ALREADY_EXIST);

        return new FileListResult(Status.SUCCESS,Objects.requireNonNull(file.listFiles()));
    }
    /**
     * 下载文件
     * @param userId 操作者id
     * @param filepath 文件地址，userId/....
     * @param response HttpServletResponse
     * @author: 本小蛋
     * @time: 2021/9/3 22:26
     */
    void downloadFile(String userId,String filepath, HttpServletResponse response){
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=UTF-8");
        response.setContentType("APPLICATION/OCTET-STREAM");
        response.setHeader("Content-Disposition", "attachment; filepath=\"" + filepath + "\"");

        PrintWriter out = null;
        try {
            out = response.getWriter();
            File file=new File(Settings.rootsDir+"/"+userId+filepath);
            FileInputStream fileInputStream = new FileInputStream(file);

            int i = 0;
            while ((i = fileInputStream.read()) != -1) {
                out.write(i);
            }
            fileInputStream.close();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * 添加一个共享文件夹
     * @param share 共享实例
     * @return: INPUT_NOT_REGULATED,NOT_FOUND,ALREADY_EXIST,SUCCESS,SYSTEM_ERROR
     * @author: 本小蛋
     * @time: 2021/9/6 22:20
     */
    public int addShare(Share share){
        share.path=share.path.replace("\\","/");
        //检查目录，密码等字符串是否有效
        if (!Judges.judgePath(share.path)||!Judges.judgeSharePw(share.password)||
                !Judges.judgePermission(share.permissions)) return Status.INPUT_NOT_REGULATED;
        //检查要共享的文件/文件夹是否存在
        if (!getFileByIdPath(share.userId,share.path).exists()) return Status.FILE_NOT_FOUND;
        //检查共享的目录是否存在
        if (shareDao.findShareByIdPath(share.userId,share.path)==null) return Status.FILE_ALREADY_EXIST;
        //添加共享
        if (shareDao.addShare(share)==1){
            FileLogUtils.addLog(share.userId,FileLogUtils.ACTION_SHARE_ADD,share.path);//打日志
            return Status.SUCCESS;
        }
        return Status.SYSTEM_ERROR;
    }
    /**
     *  使用 用户id 获取共享文件列表
     * @param userId 用户id
     * @return: null：没有打开共享开关或文件不存在
     * @author: 本小蛋
     * @time: 2021/9/8 10:06
     */
    public ArrayList<Share> getSharesById(String userId){
        BufferedReader br =FileUtils.getShareFilesBufReader(userId);
        if (br==null) return null;
        String line;
        ArrayList<Share> list=new ArrayList<>();
        try {
            while (true) {
                if ((line = br.readLine()) != null){
                    list.add(new Share(line));
                }
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 创建文件夹
     * @param userId 创建者id，id=null时为匿名
     * @param source 目标
     * @return Status：SUCCESS,FAIL,INPUT_NOT_REGULATED,NOT_GRANTED
     */
    public int mkDir(String userId,MyPath source){
        print("mkdir",userId,source.toString());
        if (!source.isAvailable())return Status.INPUT_NOT_REGULATED;
        String filePath=source.sourcePath;
        if (source.diskId==null||source.diskId.equals(userId)){//访问自己的
            filePath=userId+source.detailPath;
        }else {//共享盘
            int grant=judgeGranted(userId,false,source.diskId,source.detailPath);
            if (grant!=Status.SUCCESS) return grant;
        }

        //权限通过
        if (FileUtils.mkdir(filePath))
            FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_MKDIR,filePath);//写日志
        else return Status.FAIL;
        return Status.SUCCESS;

    }

    /**
     * 删除文件夹/文件
     * @param userId 删除者id，id=null时为匿名
     * @param source 目标
     * @return Status：SUCCESS,FAIL,INPUT_NOT_REGULATED,SHARE_NOT_GRANTED
     */
    public int delFile(String userId,MyPath source){
        print("delDir",userId,source.toString());
        if (!source.isAvailable())return Status.INPUT_NOT_REGULATED;

        String filePath=source.sourcePath;
        if (source.diskId==null||source.diskId.equals(userId)){//访问自己的
            filePath=userId+source.detailPath;
        }else {//共享盘
            int grant=judgeGranted(userId,false,source.diskId,source.detailPath);
            if (grant!=Status.SUCCESS) return grant;
        }
        //权限通过
        if (FileUtils.deleteFile(filePath))
            FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_DELETE,filePath);
        else return Status.FILE_NOT_FOUND;
        return Status.SUCCESS;
    }

    /**
     * 批量删除
     * @param userId 操作者id
     * @param sources 文件地址列表
     * @return 一串Status，下标0为第1个文件的删除结果
     */
    public String delFiles(String userId, String[] sources) {
        StringBuilder res= new StringBuilder();
        for (String source:sources){
            MyPath sourcePath=new MyPath(sources[0]);
            res.append(delFile(userId, sourcePath));
        }
        return res.toString();
    }

    /**
     * 批量移动
     * @param userId 操作者id
     * @param sources 文件地址列表
     * @return 一串Status，下标0为第1个文件的操作结果
     */
    public String moveFiles(String userId, String[] sources,MyPath target) {
        if (!target.isAvailable()) return ""+Status.INPUT_NOT_REGULATED;
        StringBuilder res= new StringBuilder();
        for (String source:sources){
            MyPath sourcePath=new MyPath(sources[0]);
            res.append(renameAndCopyFile(userId, sourcePath,target,false));
        }
        return res.toString();
    }
    /**
     * 批量复制
     * @param userId 操作者id
     * @param sources 文件地址列表
     * @return 一串Status，下标0为第1个文件的操作结果
     */
    public String copyFiles(String userId, String[] sources,MyPath target) {
        if (!target.isAvailable()) return ""+Status.INPUT_NOT_REGULATED;
        StringBuilder res= new StringBuilder();
        for (String source:sources){
            MyPath sourcePath=new MyPath(sources[0]);
            res.append(renameAndCopyFile(userId, sourcePath,target,true));
        }
        return res.toString();
    }
    /**
     * 重命名/移动
     */
    public int renameFile(String userId,MyPath source,MyPath target){
        return renameAndCopyFile(userId, source, target, false);
    }
    /**
     * 复制
     */
    public int copyFile(String userId,MyPath source,MyPath target){
        return renameAndCopyFile(userId, source, target, true);
    }
    /**
     * 复制和重命名、移动在一起
     */
    private int renameAndCopyFile(String userId,MyPath source,MyPath target,boolean copy){
        if (!source.isAvailable()||!target.isAvailable()) return Status.INPUT_NOT_REGULATED;
        String sourcePath=source.sourcePath;
        String targetPath=target.sourcePath;
        if (source.diskId==null||source.diskId.equals(userId)){//访问自己的
            sourcePath=userId+source.detailPath;
            targetPath=userId+target.detailPath;
        }else {//共享盘
            int grant=judgeGranted(userId,true,source.diskId,source.detailPath);
            if (grant!=Status.SUCCESS) return grant;
        }
        if (FileUtils.renameFile(sourcePath,targetPath,copy))
            FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_RENAME,sourcePath+":"+targetPath);
        else return Status.FILE_NOT_FOUND;
        return Status.SUCCESS;
    }

    public int judgeGranted(String userId,boolean read,String pathWithId) {
        int index=pathWithId.indexOf('/');
        if (index==-1) return Status.INPUT_NOT_REGULATED;
        return judgeGranted(userId,read,pathWithId.substring(0,index),pathWithId.substring(index+1));
    }
    /**
     *
     * @param userId 操作者id，匿名则为null
     * @param read 读还是写
     * @param userId 目标 id
     * @param path 目录
     * @return: SUCCESS,SHARE_NOT_GRANTED,FILE_NOT_SHARE
     * @author: 本小蛋
     * @time: 2021/9/6 21:37
     */
    public int judgeGranted(String userId,boolean read,String targetId,String path) {
        boolean anon=userId==null;
        File file = Paths.get(Settings.usersDir, userId, Share.shareOpenOpen).toFile();
        if (!FileUtils.isShareOpen(userId))return Status.SHARE_NOT_OPEN;

        Share share = shareDao.findShareByIdPath(targetId,path);
        if (share==null) return Status.FILE_NOT_SHARE;
        String permissions= share.permissions;
        //01-34-67
        if (anon){//匿名访问
            //匿名读,匿名写
            if ((read&&permissions.charAt(0)=='1')||(!read&&permissions.charAt(1)=='1')) return  Status.SUCCESS;
            return  Status.SUCCESS;//
        }else {//用户访问
            //匿名权限开了的话就不用判断用户权限了
            if (read) return (permissions.charAt(0) == '1' || permissions.charAt(3) == '1') ? Status.SUCCESS:Status.SHARE_NOT_GRANTED;
            else return (permissions.charAt(1) == '1' || permissions.charAt(4)=='1' )? Status.SUCCESS:Status.SHARE_NOT_GRANTED;
        }
    }

    /**
     * 获取 roots 文件夹中的文件
     * @param userId 用户id
     * @param path 从用户根目录开始
     * @return
     */
    private File getFileByIdPath(String userId,String path){
        return Paths.get(Settings.rootsDir,userId,path).toFile();
    }
    private void print(String ... args ){
        System.out.print("------> FileService: ");
        for (String line : args) System.out.print(line+" ");
        System.out.println("");
    }

    /**
     * 上传文件
     * @param userId 上传者id
     * @param part part
     */
    public int uploadFile(String userId, MyPath path,Part part) {
        if (!path.isAvailable()) return Status.INPUT_NOT_REGULATED;
        String realFileName=part.getSubmittedFileName();
        File recvFile;
        System.out.println(realFileName);
        //授权判断
        if (path.diskId==null){
            recvFile=Paths.get(Settings.rootsDir,userId,path.detailPath,realFileName).toFile();
        }else {
            int grant=judgeGranted(userId,false,path.diskId,path.detailPath);
            if (grant==Status.SUCCESS)  recvFile=Paths.get(Settings.rootsDir,path.sourcePath,realFileName).toFile();
            else return grant;
        }
        if (recvFile.exists()) return Status.FILE_ALREADY_EXIST;//已存在
        if (!recvFile.getParentFile().exists()) return Status.FILE_NOT_FOUND;//目录不存在
        //接收文件
        try(
            InputStream is = part.getInputStream();
            FileOutputStream fos = new FileOutputStream(recvFile);
        ){
            byte[] bty = new byte[1024];
            int length = 0;
            while ((length = is.read(bty)) != -1) {
                fos.write(bty, 0, length);
            }
            //日志
            FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_UPLOAD,path.sourcePath+"/"+realFileName);
            return Status.SUCCESS;
        }catch (Exception e){
            e.printStackTrace();
            if (recvFile.exists()) recvFile.delete();
            return Status.FAIL;
        }
    }

    /**
     * 接收上传的文件
     * @param userId 上传者id
     * @param fileUploadData chunks有值则为分片，否则不分片
     * @return INPUT_NOT_REGULATED，FILE_ALREADY_EXIST ，SYSTEM_ERROR，SUCCESS，FAIL，FILE_NOT_FOUND
     */
    public int uploadFile(String userId, FileUploadData fileUploadData) {
        MyPath path= new MyPath(fileUploadData.path);
        if (!path.isAvailable()) return Status.INPUT_NOT_REGULATED;
        //授权判断
        if (path.diskId!=null){
            int grant=judgeGranted(userId,false,path.diskId,path.detailPath);
            if (grant!=Status.SUCCESS) return grant;
        }
        //判断文件是否存在
        File target = Paths.get(Settings.rootsDir,path.diskId==null? userId:path.diskId,path.detailPath,fileUploadData.name).toFile();
        if (target.exists())return Status.FILE_ALREADY_EXIST;
        if (!target.getParentFile().exists()) return Status.INPUT_NOT_REGULATED;//文件夹不存在

        //查询md5

        //分片上传
        if (fileUploadData.isChunked()){
            //接收分片文件的临时目录
            File tempDir= Paths.get(Settings.tempFilesDir,fileUploadData.guid).toFile();
            if (!tempDir.isDirectory()) tempDir.mkdirs();
            //开始接收
            if (FileUtils.writeInputStreamToFile(fileUploadData.getInputStream(),new File(tempDir,fileUploadData.chunk+""))){
                //接收成功，判断，是否为最后一片
                if (fileUploadData.isEnd()){
                    //判断其它分片是否接收完成
                    try {
                        int cnt=0;
                        while (Objects.requireNonNull(tempDir.listFiles()).length!=fileUploadData.chunks&&cnt++<10) {
                            System.out.println("其它分片还没下载完成，等待500毫秒！listFiles："+tempDir.listFiles().length);
                            Thread.sleep(500);
                        }
                        if (Objects.requireNonNull(tempDir.listFiles()).length!=fileUploadData.chunks)
                            return Status.FILE_NOT_FOUND;//有分片下载未成功
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    File [] list=new File[fileUploadData.chunks];
                    for (int i=0;i<fileUploadData.chunks;++i)list[i]=new File(tempDir,""+i);
//                    Arrays.sort(list);
                    //合并
                    if (FileUtils.mergeFiles(list,target)){
                        FileUtils.deleteDir(tempDir);//删除分片文件
                        FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_UPLOAD,path.sourcePath+"/"+fileUploadData.name);
                        return Status.SUCCESS;
                    }
                    return Status.SYSTEM_ERROR;//合并失败

                }
                return Status.SUCCESS;//继续接收
            }
            return Status.FAIL;//接收失败
        }else {//不分片
            if (FileUtils.writeInputStreamToFile(fileUploadData.getInputStream(),target)){
                FileLogUtils.addLog(userId,FileLogUtils.ACTION_FILE_UPLOAD,path.sourcePath+"/"+fileUploadData.name);
                return Status.SUCCESS;
            }
            return Status.FAIL;
        }
    }
}
